﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace d39
{
    [Serializable]
    public struct HalfCoordinate
    {
        public HalfCoordinate(int doubleX, int doubleY)
        {
            DoubleX = doubleX;
            DoubleY = doubleY;
        }

        public readonly int DoubleX;
        public readonly int DoubleY;

        public static HalfCoordinate Parse(string str, int start, out int end)
        {
            var x = Base26Point5.ParseDoubleInt(str, start, out end);
            var y = Base26Point5.ParseDoubleInt(str, end, out end);
            return new HalfCoordinate(x, y);
        }

        public override string ToString()
        {
            return Base26Point5.DoubleIntToString(DoubleX) + Base26Point5.DoubleIntToString(DoubleY);
        }

        public static HalfCoordinate ParseHumanReadable(string str)
        {
            var pos = str.IndexOfAny("-0123456789".ToCharArray(), 1);
            int doubleY = int.Parse(str.Substring(pos).TrimEnd('+')) * 2 + (str.EndsWith("+") ? 1 : 0);
            str = str.Substring(0, pos);
            bool xIsOdd = str.EndsWith("+");
            str = str.TrimEnd('+');
            str = str.Substring(0, str.Length - 1).ToUpper() + str.Substring(str.Length - 1).ToLower();
            int end;
            int doubleX = Base26.ParseInt(str.Replace('-', '_'), 0, out end) * 2 + (xIsOdd ? 1 : 0);
            return new HalfCoordinate(doubleX, doubleY);
        }

        public string ToHumanReadableString()
        {
            return Base26.IntToString(halve(DoubleX)).ToUpper().Replace('_', '-') + oddPlus(DoubleX) +
                halve(DoubleY) + oddPlus(DoubleY);
        }
        private static int halve(int n)
        {
            return (n - (isOdd(n) ? 1 : 0)) / 2;
        }
        private static bool isOdd(int n)
        {
            return n % 2 != 0;
        }
        private static string oddPlus(int n)
        {
            return isOdd(n) ? "+" : "";
        }

        public static implicit operator HalfCoordinate(Coordinate coord)
        {
            return new HalfCoordinate(coord.X * 2, coord.Y * 2);
        }
        public static explicit operator Coordinate(HalfCoordinate coord)
        {
            if (isOdd(coord.DoubleX) || isOdd(coord.DoubleY)) throw new InvalidCastException(coord.ToHumanReadableString() + " is not a whole-numbered coordinate");
            return new Coordinate(coord.DoubleX / 2, coord.DoubleY / 2);
        }
        public static HalfCoordinate operator +(HalfCoordinate coord, Vector v)
        {
            return new HalfCoordinate(coord.DoubleX + v.X * 2, coord.DoubleY + v.Y * 2);
        }
        public static HalfCoordinate operator -(HalfCoordinate coord, Vector v)
        {
            return new HalfCoordinate(coord.DoubleX - v.X * 2, coord.DoubleY - v.Y * 2);
        }
        public static bool operator ==(HalfCoordinate a, HalfCoordinate b)
        {
            return a.DoubleX == b.DoubleX && a.DoubleY == b.DoubleY;
        }
        public static bool operator !=(HalfCoordinate a, HalfCoordinate b)
        {
            return a.DoubleX != b.DoubleX || a.DoubleY != b.DoubleY;
        }
        public override bool Equals(object obj)
        {
            return obj is HalfCoordinate && this == ((HalfCoordinate)obj);
        }
        public override int GetHashCode()
        {
            return DoubleX.GetHashCode() ^ DoubleY.GetHashCode();
        }
    }
}